#include "filter.h"

template <typename T>

T mymax(T a,T b)
{
	return (b<a) ? a : b;
}

template <typename T>
T mymin(T a,T b)
{
	return (b>a) ? a : b; 
}

/*
void apply_allpixel(T (*pf)(T), image<T> &image)// go through image and manipulate all pixel with function
{
	for(int x=0; x < image.dim(0); ++x)
	{
		for(int y=0; y < image.dim(1); ++y)
		{
			image(x,y)=pf(image(x,y));
		}
	}
};*/

template <typename T>
void getMinMaxImage(const Image<T> &in, T &min, T &max)
{
	T curr_max = numeric_limits<T>::min();
	T curr_min = numeric_limits<T>::max();
	T curr_value = 0;
	for(int x=0; x < in.dim(0); ++x)
	{
		for(int y=0; y < in.dim(1); ++y)
		{
			curr_value = in(x,y);
			if(curr_max < curr_value)curr_max=curr_value;
			if(curr_min > curr_value)curr_min=curr_value;
		}
	}
	min = curr_min;
	max = curr_max;
};


// Convolves input with mask and stores result in output
void filter(
  const pip::Image<float> &input0, 
  pip::Image<float> &output, 
  const pip::Image<float> &mask, 
  const pip::BorderInfo &bi )
{
	//get how far the kernel reaches beyond the boarder
	int kernhalf_x = mask.dim(0)/2;
	int kernhalf_y = mask.dim(1)/2;
	//get necessary maximal border-size
	int needed_border = int(mymax(kernhalf_x,kernhalf_y));
	Image<float> input(Dimension(input0.dim(0),input0.dim(1)),needed_border,bi);
	for(int copy_x=0; copy_x < input0.dim(0); ++copy_x)  // I could not run Image.bordered on a copy of input (and input itself is const... )
	{													 // therefore i have to copy the hard way 
		for(int copy_y=0; copy_y < input0.dim(1); ++copy_y)
		{
			input(copy_x,copy_y)=input0(copy_x,copy_y);
		}
	}
	//check which image has the smaller dimensions SO we dont try to acess non existing points
	int x_limit = mymin(input.dim(0),output.dim(0));
	int y_limit = mymin(input.dim(1),output.dim(1));
	for(int x =0; x < x_limit;++x)
	{
		for(int y=0; y < y_limit;++y)
		{
			double sum=0;
			int minus_x_kern=mask.dim(0);
			for(int x_kern=-kernhalf_x; x_kern < kernhalf_x+1; ++x_kern)
			{
				--minus_x_kern;
				int minus_y_kern=mask.dim(1);
				for(int y_kern=-kernhalf_y; y_kern < kernhalf_y+1; ++y_kern)
				{
					--minus_y_kern;
					sum += input(x_kern+x,y_kern+y) * mask(minus_x_kern,minus_y_kern);
				}
			}
			if (sum > double(numeric_limits<float>::max()) || sum < double(-numeric_limits<float>::max()))
			{
				cerr <<"  \n data-loss in filter. float(double) is: " 
					 << sum 
					 << endl;
			}
			output(x,y)=float(sum);
		}
	}
};


void filterSeparable(
  const pip::Image<float> &in, 
  pip::Image<float> &out, 
  const std::vector<std::vector<float> > &kernels, 
  const pip::BorderInfo &bi )
{
	//cout << kernels.size() << endl;
	//cout << kernels[0].size() << endl;
	//cout << kernels[1].size() << endl;
	Image<float> mask1(Dimension(int(kernels[0].size()) ,1));
	Image<float> mask2(Dimension(1,int(kernels[1].size())));
	Image<float> tmp(out);
	for(int j=0; j < kernels[0].size(); ++j)
	{
		mask1(j,0)=kernels[0][j];
	}
	for(int j=0; j < kernels[1].size(); ++j)
	{
		mask2(0,j)=kernels[1][j];
	}
	cout << "mask1:\n";
	mask1.print(cout);
	cout << "mask2:\n";
	mask2.print(cout);
	filter(in,tmp,mask1);
	filter(tmp,out,mask2);
}







void imageconvert(const Image<float> &input, Image<unsigned char> & output)
{
	float min = 0;
	float max = 0;
	float min_weight = -1 * min;
	getMinMaxImage(input,min,max);
	if( max < 0.001 && max > -0.001) max=float(0.001);
	for(int x=0; x < input.dim(0); ++x)
	{
		for(int y=0; y < input.dim(1); ++y)
		{
			output(x,y) = unsigned char( 255 * (min_weight + input(x,y))/max);
		}
	}
};


void imageconvert(const Image<unsigned char> &input, Image<float> &output)
{
	for(int x=0; x < input.dim(0); ++x)
	{
		for(int y=0; y < input.dim(1); ++y)
		{
			output(x,y) = float(input(x,y));
		}
	}
};

void loadimage(string name, Image<float> &loaded)
{
	Image<unsigned char> load;
	if(!importFile(load,name)) cerr << "\n !!! loadimage: not imported !!! \n";
	Image<float> tmp(Dimension(load.dim(0),load.dim(1)));
	imageconvert(load,tmp); 
	loaded = tmp;
};

void saveimage(string name, Image<float> &save)
{
	Image<unsigned char> saved(Dimension(save.dim(0),save.dim(1)));
	imageconvert(save, saved); 
	if(!exportFile(saved,name)) cerr << "\n !!! saveimage: not exported !!! \n";
};


// Samples the unnormalized Gaussian for given sigma at integer values
float g0(const int &i, const float &sigma)
{
  return (float)(exp(-(float)(i * i) / (2.0 * sigma * sigma)));
}

// Samples the unnormalized first derivative of the Gaussian for given sigma at integer values
float g1(const int &i, const float &sigma)
{
	return (float) (float(i)/(sigma*sigma)  * -g0(i,sigma));
}

// Samples the unnormalized second derivative of the Gaussian for given sigma at integer values
float g2(const int &i, const float &sigma)
{
	return (float) (float(1)/(sigma*sigma)  * -g0(i,sigma)+ (-g1(i,sigma)* float(i)/(sigma*sigma)));
}

// Samples the unnormalized third derivative of the Gaussian for given sigma at integer values
float g3(const int &i, const float &sigma)
{
 return (g1(i,sigma) + (g2(i,sigma)* (float(i)/(sigma*sigma))) + (g1(i,sigma)* float(1)/(sigma*sigma)))/2; //sneaky and propably wrong
}




/*
void exercise_1(){
	cout << "\n Exercise 1 \n";
	Image<unsigned char> mirrored(Dimension(3,3), 2, border_mirror);
	Image<unsigned char> clamped(Dimension(3,3), 2, border_clamp);
	for(int i = 0 ; i < 9; ++i)
	{
		clamped(i%3, i/3)=mirrored(i%3, i/3)= i+1;

	}
	cout << "\n clamped border with width 2:\n";
	for(int i = 0 ; i < 49; ++i)
	{
		cout << (int)clamped(i%7-2, i/7-2) << " ";
		if (i%7==6) cout << endl;
	}
	cout << endl;
	system("pause");
	cout << "\n mirrored border with width 2:\n";
	for(int i = 0 ; i < 49; ++i)
	{
		cout << (int)mirrored(i%7-2, i/7-2) << " ";
		if (i%7==6) cout << endl;
	}
	cout << endl;
	system("pause");
};*/


